package com.uam.core.business;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.uam.core.cache.PointCacheConfig;
import com.uam.core.dao.elasticsearch.ElasticsearchDao;
import com.uam.core.dao.mysql.StationDao;
import com.uam.core.dao.mysql.StationModel;
import com.uam.core.dao.mysql.TaskDao;
import com.uam.core.entity.PointInfoData;
import com.uam.core.entity.constant.RedisConstants;
import com.uam.core.entity.enums.BaseStationTypes;
import com.uam.core.entity.enums.ElasticsearchIndices;
import com.uam.core.entity.model.AircraftModel;
import com.uam.core.entity.model.ElasticsearchQueryResult;
import com.uam.core.entity.model.RedisAltitudeInfo;
import com.uam.core.entity.model.TaskModel;
import com.uam.core.lk.utils.fencebuffer.BufferBase;
import com.uam.core.lk.utils.fencebuffer.PolygonBufferImpl;
import com.uam.core.scheduler.ConflictResolution;
import com.uam.core.scheduler.PointInfoScheduler;
import com.uam.core.service.BaseStationService;
import com.uam.core.service.RouteService;
import com.uam.core.tool.GetOldPointTool;
import com.uam.core.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Point;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 每个点信号强度处理
 */
@Slf4j
@Service
public class PointSignal extends ParentBusiness {
    @Autowired
    private ConflictResolution conflictResolution;
    @Autowired
    private PointCacheConfig pointCacheConfig;

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private WebSocketServer socketServer;

    @Autowired
    private TaskDao taskDao;

    @Autowired
    private RouteService routeService;

    @Autowired
    private ElasticsearchDao elasticsearchDao = null;

    @Autowired
    private BaseStationService baseStationService;

    private PolygonBufferImpl polygonBuffer = new PolygonBufferImpl();

    @Autowired
    private StationDao stationDao = null;

    @Autowired
    private OperationDbBusiness operationDbBusiness;


    private Map<String, Object> getDataMap(PointInfoData pointInfoDataLast) throws IOException {
        Map<String, Double> coordinate = new HashMap<String, Double>();
        Map<String, Object> data = new HashMap<String, Object>();
        coordinate.put(ElasticsearchQueryResult.CommonFields.LON.getName(), pointInfoDataLast.getLon());
        coordinate.put(ElasticsearchQueryResult.CommonFields.LAT.getName(), pointInfoDataLast.getLat());
        data.put(ElasticsearchQueryResult.CommonFields.POSITION.getName(), coordinate);
        data.put("id", pointInfoDataLast.getId());
        routeService.setSignalInfos(pointInfoDataLast.getLon(), pointInfoDataLast.getLat(), pointInfoDataLast.getSafeDistance(), data);
        System.out.println("入库之前：" + JSONObject.toJSONString(pointInfoDataLast));
        System.out.println("入库之后：" + JSONObject.toJSONString(data));
        socketServer.broadCastInfo(pointInfoDataLast, pointInfoDataLast.getTaskId());
        return data;
    }

    private int getNumCo(String num){
        int row=0;
       try {
           row= Integer.valueOf(num);
       }catch (Exception e){

       }
       return row;
    }

    /**
     * 检查点信号强度
     *
     * @param id
     */
    public void checkPointSignal(String id) {
        Map<String, Object> map = null;
        routeService.getAllBaseStations();

        for (int i = 0; i < PointInfoScheduler.signalStrength.size(); i++) {
            Map<String, Object> temMap = PointInfoScheduler.signalStrength.get(i);
            if (temMap.get("id").equals(id)) {
                map = temMap;
                break;
            }
        }
        // map 等于空，是关闭基站
        if (map == null) {
            map = baseStationService.detail(id);
        }

        if (map == null) {
            log.warn("更新错误：基站id不存在 id：{}", id);
            return;
        }
        Set<Integer> takIds = pointCacheConfig.getAll().asMap().keySet();
        for (Integer takId : takIds) {
            try {
                long nowTime = System.currentTimeMillis();
                Map<String, PointInfoData> dataMap = pointCacheConfig.get(takId);
                if (dataMap == null || dataMap.size() == 0) {
                    continue;
                }
                List<PointInfoData> pointInfoData = new ArrayList<>(dataMap.values());
                int row = pointInfoData.size();

                PointInfoData endTimeData = pointInfoData.get(row - 1);
                if (nowTime > endTimeData.getTime()) {
                    continue;
                }
                PointInfoData pointInfoData1 = TimeToPointIndex.checkPointDataConn(pointInfoData, nowTime, takId);
                if (pointInfoData1 == null) {
                    continue;
                }
                List<PointInfoData> sourcePointInfoDataSyb = pointInfoData.subList(pointInfoData1.getIndex(), row - 1);


                Map<String, Object> pointPosition = (Map<String, Object>) map.get("position");
                double pointLon = ((Double) pointPosition.get("lon")).doubleValue();
                double pointLat = ((Double) pointPosition.get("lat")).doubleValue();
                double range = Double.valueOf(map.get("range") + "");

                Geometry bufferedGeom = BufferBase.geometryFactory.createPoint(new Coordinate(pointLon, pointLat));
                Geometry geometryBuffer = polygonBuffer.getBuff(bufferedGeom, 1, range);

                List<PointInfoData> dataLine = new ArrayList<>(1002);
                sourcePointInfoDataSyb.forEach(a -> {
                    if (geometryBuffer.contains(BufferBase.geometryFactory.createPoint(new Coordinate(a.getLon(), a.getLat())))) {
                        dataLine.add(a);
                    }
                });
                //在圆内所有点
                if (dataLine.size() == 0) {
                    //无变化任务
                    continue;
                }

                //重新计算信号前度
                boolean isAirDown = false;
                PointInfoData dowPoint = null;
                List<PointInfoData> dataLineTemp=new ArrayList<>(1024);
                for (int i = 0; i < dataLine.size(); i++) {
                    dowPoint = dataLine.get(i);
                    Map<String, Object> data = new java.util.HashMap<>();
                    data.put("id", dowPoint.getId());
                    routeService.setSignalInfos(dowPoint.getLon(), dowPoint.getLat(), dowPoint.getSafeDistance(), data);
                    int communicationSignalNum = getNumCo(data.get("communication_signal_num") + "");
                    int supervisorySignalNum = getNumCo((data.get("supervisory_signal_num") )+"" );
                    int navigationSignalNum = getNumCo(data.get("navigation_signal_num")+"");
                    dataLineTemp.add(dowPoint);
                    // 导航信号数量  监督信号数量  导航_信号 按照类型来划分
                    int type = ((Integer) map.get(ElasticsearchQueryResult.CommonFields.TYPE.getName())).intValue();
                    //通信
                    if (BaseStationTypes.COMMUNICATION.getCode() == type) {
                        if(communicationSignalNum == 0){
                            isAirDown = true;
                            break;
                        }
                    //导航
                    } else if (BaseStationTypes.NAVIGATION.getCode() == type) {
                        if(navigationSignalNum == 0){
                            isAirDown = true;
                            break;
                        }
                    //监督信号数量
                    } else if (BaseStationTypes.SUPERVISORY.getCode() == type) {
                        if(supervisorySignalNum == 0){
                            isAirDown = true;
                            break;
                        }
                    }
                }

                //只有任务飞行中，而且其他基站都关闭了，才需要强制降落
                if (isAirDown && "0".equals(map.get("status")+"")) {
                    //todo 强制降落
                    PointInfoData deleteInfo2 = sourcePointInfoDataSyb.get(sourcePointInfoDataSyb.size() - 1);
                    PointInfoData deleteInfo3 = sourcePointInfoDataSyb.get(sourcePointInfoDataSyb.size() - 2);
                    Map<String, Object> deleteData = new HashMap<>();
                    deleteData.put("task_id", takId);
                    this.elasticsearchDao.delByQuery(ElasticsearchIndices.ROUTE_COMPONENT_INDEX.getName(), deleteData, "own_time", dowPoint.getTime()+1000, null);

                    //找到最近的起降点
                    StationModel stationModel = getNearestPoint(dowPoint);
                    System.out.println("name:" + stationModel.getPointName() + ", lonlat:" + stationModel.getPoint().getX() + "," + stationModel.getPoint().getY());

                    //直线距离行驶
                    JSONArray jsonArray = new JSONArray();
                    JSONArray jsonArray01 = new JSONArray(6);
                    jsonArray01.add(dowPoint.getLon());
                    jsonArray01.add(dowPoint.getLat());
                    jsonArray01.add(0);


                    JSONArray jsonArray02 = new JSONArray(6);
                    jsonArray02.add(stationModel.getPoint().getX());
                    jsonArray02.add(stationModel.getPoint().getY());
                    jsonArray02.add(0);

                    jsonArray.add(jsonArray01);
                    jsonArray.add(jsonArray02);

                    RedisAltitudeInfo redisAltitudeInfo = (RedisAltitudeInfo) redisTemplate.opsForValue().get(RedisConstants.TASK_MODEL_INFO + takId);
                    redisAltitudeInfo.getTaskModel().setStartTime(new Timestamp(System.currentTimeMillis() + 5000));
                    List<Map<String, Object>> data1 = operationDbBusiness.setUpdataDbCheck(jsonArray, redisAltitudeInfo);

                    List<Map<String, Object>> dataNewList = new ArrayList<>();
                    Map<String, Object> map3 = data1.get(data1.size() - 1);
                    map3.put("id", deleteInfo2.getId());
                    dataNewList.add(map3);

                    this.elasticsearchDao.saveBatchData(ElasticsearchIndices.ROUTE_COMPONENT_INDEX.getName(), dataNewList);
                    this.taskDao.updateTaskSingel(2, takId);
                    broadCastInfo(dataNewList, redisAltitudeInfo.getTaskModel());
                    //降落不参与避让
                    pointCacheConfig.del(takId);
                    redisTemplate.delete(RedisConstants.ROUTE_POINTS + takId);
                    redisTemplate.delete(RedisConstants.TASK_MODEL_INFO + takId);

                    long sTime=Long.valueOf(data1.get(0).get("own_time")+"");
                    long eTime=Long.valueOf(map3.get("own_time")+"");
                    long t=eTime-sTime;
                    System.out.println("sTime:"+sTime+",eTime:"+eTime+"  "+t);
                    redisTemplate.opsForValue().set(RedisConstants.ROUTE_AIR_TASK+takId,t,t, TimeUnit.MILLISECONDS);
                    continue;
                }

                List<Map<String, Object>>   dataMapSingle = new ArrayList<>();
                for (int i = 0; i < dataLine.size(); i++) {
                    PointInfoData pointDataInfo = dataLine.get(i);
                    Map<String, Object> data = new java.util.HashMap<>();
                    data.put("id", pointDataInfo.getId());
                    routeService.setSignalInfos(pointDataInfo.getLon(), pointDataInfo.getLat(), pointDataInfo.getSafeDistance(), data);
                    dataMapSingle.add(data);
                }

                //更新信号强度和数据库
                this.elasticsearchDao.updatePropJsonById(ElasticsearchIndices.ROUTE_COMPONENT_INDEX.getName(), dataMapSingle);
                //跟新数据库,
                this.taskDao.updateTaskSingel(1, takId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void broadCastInfo(List<Map<String, Object>> data1, TaskModel task) throws IOException {
        data1.forEach(map -> {
            Map map1 = (Map) map.get("position");
            PointInfoData pointInfoDataLast = new PointInfoData(
                    Long.valueOf(map.get("own_time") + ""),
                    Double.valueOf(map1.get("lon") + ""),
                    Double.valueOf(map1.get("lat") + ""),
                    Double.valueOf(map.get(ElasticsearchQueryResult.CommonFields.ALTITUDE.getName()) + ""),
                    Integer.valueOf(map.get("index") + ""), Double.valueOf(map.get("last_position_distance") + " "),
                    task.getName(), map1.get("id") + "");
            try {
                socketServer.broadCastInfo(pointInfoDataLast, task.getId());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 计算每个轨迹点飞行的时间戳
     *
     * @param aircraft
     * @param track
     * @return
     */
    public JSONArray pointsTime(AircraftModel aircraft, JSONArray track, TaskModel taskModel) {
        getHistoryTime(track, aircraft.getSpeed(), taskModel, 0, true);
        return track;
    }

    public StationModel getNearestPoint(PointInfoData deleteInfo) {
        StationModel model = null;
        double minDistance = Double.MAX_VALUE;
        Point bufferedGeom = BufferBase.geometryFactory.createPoint(new Coordinate(deleteInfo.getLon(), deleteInfo.getLat()));
        List<StationModel> stationModelList = stationDao.queryStationModel();
        //从数组中找回距离追端的对象
        for (int i = 0; i < stationModelList.size(); i++) {
            StationModel stationModel = stationModelList.get(i);
            double distance = bufferedGeom.distance(stationModel.getPoint());
            if (distance < minDistance) {
                minDistance = distance;
                model = stationModel;
            }
        }
        return model;
    }
}
